#include <stdio.h>
#include <stdlib.h> 
#include <time.h>
#include <string.h>

#define MAXDEPTH 4 //MAXDEPTH: the maximum branch level to generate tables to.
#define TURNTYPE 2 //TURNTYPE: 1 for face-turn metric, 2 for quarter-turn metric.
#define EDGEFILE   "edgeTable.txt"   //EDGEFILE: name of file to save tables to.
#define CORNERFILE "cornerTable.txt" //CORNERFILE: name of file to save tables to.

#define C1 0
#define C2 1
#define C3 2
#define C4 3
#define C5 4
#define C6 5
#define C7 6
#define C8 7
#define E9 8
#define E10 9
#define E11 10
#define E12 11
#define E13 12
#define E14 13
#define E15 14
#define E16 15
#define E17 16
#define E18 17
#define E19 18
#define E20 19


/*******************************************************************/
/************************** Declarations ***************************/
/*******************************************************************/

struct aCube {
  char r;
  char w;
  char b;
  char o;
  char y;
  char g;
};

void allRotations();
void resetCube( struct aCube *);
void scramble( struct aCube *, short);
void printStepCube( struct aCube *);
void printCrossCube( struct aCube *);
void printEdges( struct aCube *);
void saveEdgeCubie( struct aCube *, FILE*, short);
void saveCornerCubie( struct aCube *, FILE*, short);

void copyCube( struct aCube *, struct aCube *);
void resetCube( struct aCube *);
short getEdgeHeuristic( struct aCube *, FILE*);
short getCornerHeuristic( struct aCube *, FILE*);
short getHeuristic( struct aCube *);

void generateTables( struct aCube *);
void generateEdges( struct aCube *, FILE*, short);
void generateCorners( struct aCube *, FILE*, short);
void IDAstar(struct aCube *, short);
short DFS( struct aCube *, short, short);


/*Moves denoted in number of clockwise rotations*/

void rotate( struct aCube *, char, int);

void r1c( struct aCube *);
void r1e( struct aCube *);
void r2c( struct aCube *);
void r2e( struct aCube *);
void r3c( struct aCube *);
void r3e( struct aCube *);

void w1c( struct aCube *);
void w1e( struct aCube *);
void w2c( struct aCube *);
void w2e( struct aCube *);
void w3c( struct aCube *);
void w3e( struct aCube *);

void b1c( struct aCube *);
void b1e( struct aCube *);
void b2c( struct aCube *);
void b2e( struct aCube *);
void b3c( struct aCube *);
void b3e( struct aCube *);

void o1c( struct aCube *);
void o1e( struct aCube *);
void o2c( struct aCube *);
void o2e( struct aCube *);
void o3c( struct aCube *);
void o3e( struct aCube *);

void y1c( struct aCube *);
void y1e( struct aCube *);
void y2c( struct aCube *);
void y2e( struct aCube *);
void y3c( struct aCube *);
void y3e( struct aCube *);

void g1c( struct aCube *);
void g1e( struct aCube *);
void g2c( struct aCube *);
void g2e( struct aCube *);
void g3c( struct aCube *);
void g3e( struct aCube *);


/******************************************************************/
/************************ Main Function ***************************/
/******************************************************************/

int main() {

  //I will begin by initializing a solved cube: 
  struct aCube mainCube[20] = {
    { 'Z', 'W', 'Z', 'O', 'Z', 'G'},
    { 'Z', 'W', 'B', 'O', 'Z', 'Z'},
    { 'R', 'W', 'Z', 'Z', 'Z', 'G'},
    { 'R', 'W', 'B', 'Z', 'Z', 'Z'},
    { 'R', 'Z', 'B', 'Z', 'Y', 'Z'},
    { 'Z', 'Z', 'B', 'O', 'Y', 'Z'},
    { 'R', 'Z', 'Z', 'Z', 'Y', 'G'},
    { 'Z', 'Z', 'Z', 'O', 'Y', 'G'},
    { 'Z', 'W', 'Z', 'O', 'Z', 'Z'},
    { 'Z', 'W', 'B', 'Z', 'Z', 'Z'},
    { 'R', 'W', 'Z', 'Z', 'Z', 'Z'},
    { 'Z', 'W', 'Z', 'Z', 'Z', 'G'},
    { 'Z', 'Z', 'B', 'Z', 'Y', 'Z'},
    { 'Z', 'Z', 'Z', 'O', 'Y', 'Z'},
    { 'Z', 'Z', 'Z', 'Z', 'Y', 'G'},
    { 'R', 'Z', 'Z', 'Z', 'Y', 'Z'},
    { 'R', 'Z', 'Z', 'Z', 'Z', 'G'},
    { 'R', 'Z', 'B', 'Z', 'Z', 'Z'},
    { 'Z', 'Z', 'B', 'O', 'Z', 'Z'},
    { 'Z', 'Z', 'Z', 'O', 'Z', 'G'}
  };

  int clockTables, clockSolved;
  time_t timeStart, timeTables, timeSolved;

  (void) time(&timeStart);
  generateTables( mainCube );
  (void) time(&timeTables);
  clockTables = (int) timeTables-timeStart;

  char face[6] = { 'r', 'w', 'b', 'o', 'y', 'g' };
  scramble( mainCube, MAXDEPTH);
  printCrossCube( mainCube );
  (void) time(&timeStart);
  IDAstar( mainCube, 0 );
  (void) time(&timeSolved);
  clockSolved = (int) timeSolved-timeStart;
  printf("Generate Tables (seconds): %d\nSolve Cube (seconds):      %d\n", clockTables, clockSolved);

  return 0;
}

/*******************************************************************/
/********************** Function Definitions ***********************/
/*******************************************************************/

void IDAstar( struct aCube * oldCube, short currentDist) {
  struct aCube newCube[20] = {};
  copyCube( newCube, oldCube);
  short bound = getHeuristic( newCube );
  printf("Bound: %d\n", bound);
  while( bound != 0 ){
    bound = DFS( newCube, currentDist, bound);
  }
}

short DFS( struct aCube * oldCube, short currentDist, short bound) {
  struct aCube newCube[20] = {};
  copyCube( newCube, oldCube);

  short h = getHeuristic( newCube );
  if( h ==0 ) {
    printf( "The cube is solved!\n");
    return 0;
  }

  short f = h + currentDist;
  if( f > bound ) return f;
  
  char face[6] = { 'r', 'w', 'b', 'o', 'y', 'g' };
  short i, j, iBest, jBest;
  short best = 9999;
  struct aCube bestCube[20];
  for( i=0; i<=5; i++) {
    for( j=1; j<=3; j++) {
      struct aCube tempCube[20];
      copyCube( tempCube, newCube );
      rotate( tempCube, face[i], j);
      short thisState = getHeuristic( tempCube );
      printf("%d %d %d\n", i, j, getHeuristic( tempCube ));
      

      if( thisState < best ){
	best = thisState;
	copyCube( bestCube, tempCube );
	iBest = i;
	jBest = j;
      }
    }
  }

  copyCube( newCube, bestCube );
  DFS( newCube, currentDist+1, bound );
  printf( "%c %d\n", face[iBest], jBest );
  return 0;
}


short getHeuristic( struct aCube * theCube) {
  short cornerHeuristic, edgeHeuristic;

  FILE* cornerFile;
  FILE* edgeFile;
  cornerFile = fopen( CORNERFILE,"r");
  edgeFile   = fopen( EDGEFILE,"r");

  edgeHeuristic   = getEdgeHeuristic(theCube, edgeFile);
  cornerHeuristic = getCornerHeuristic(theCube, cornerFile);

  //printf("edgeHeuristic: %d\n", edgeHeuristic);
  //printf("cornerHeuristic: %d\n", cornerHeuristic);

  fclose(cornerFile);
  fclose(edgeFile);

  if( edgeHeuristic == 9999 || cornerHeuristic == 9999)
    return 9999;
  if (edgeHeuristic >= cornerHeuristic ) 
    return edgeHeuristic;
  else 
    return cornerHeuristic;
}

short getEdgeHeuristic( struct aCube * theCube, FILE* edgeFile) {
  int distToGoal;
  char cube[24];
  int best=9999;
  while (fscanf( edgeFile, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%d\n",//
                 &cube[0],  &cube[1],  &cube[2],  &cube[3],  &cube[4],// 
                 &cube[5],  &cube[6],  &cube[7],  &cube[8],  &cube[9],//
                 &cube[10], &cube[11], &cube[12], &cube[13], &cube[14],//       
                 &cube[15], &cube[16], &cube[17], &cube[18], &cube[19],//       
                 &cube[20], &cube[21], &cube[22], &cube[23], &distToGoal) != EOF) {
    

    if (theCube[E9].w  == cube[0]){
      if (theCube[E9].o  == cube[1]){
	if (theCube[E10].w == cube[2]){
	  if (theCube[E10].b == cube[3]){
	    if (theCube[E11].r == cube[4]){
	      if (theCube[E11].w == cube[5]){
		if (theCube[E12].w == cube[6]){
		  if (theCube[E12].g == cube[7]){
		    if (theCube[E13].b == cube[8]){
		      if (theCube[E13].y == cube[9]){
			if (theCube[E14].y == cube[10]){
			  if (theCube[E14].o == cube[11]){
			    if (theCube[E15].y == cube[12]){
			      if (theCube[E15].g == cube[13]){
				if (theCube[E16].r == cube[14]){
				  if (theCube[E16].y == cube[15]){
				    if (theCube[E17].r == cube[16]){
				      if (theCube[E17].g == cube[17]){
					if (theCube[E18].r == cube[18]){
					  if (theCube[E18].b == cube[19]){
					    if (theCube[E19].b == cube[20]){
					      if (theCube[E19].o == cube[21]){
						if (theCube[E20].o == cube[22]){
						  if (theCube[E20].g == cube[23]){
						    if(distToGoal < best)
						      best = distToGoal;
                                                  }}}}}}}}}}}}}}}}}}}}}}}}
						  }
  return best;
}

short getCornerHeuristic( struct aCube * theCube, FILE* filename) {
  int distToGoal;
  char cube[24];
  int best = 9999;
  while (fscanf( filename, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%d\n",// 
		 &cube[0],  &cube[1],  &cube[2],  &cube[3],  &cube[4],//                 
		 &cube[5],  &cube[6],  &cube[7],  &cube[8],  &cube[9],//                  
		 &cube[10], &cube[11], &cube[12], &cube[13], &cube[14],//      
		 &cube[15], &cube[16], &cube[17], &cube[18], &cube[19],//     
		 &cube[20], &cube[21], &cube[22], &cube[23], &distToGoal) != EOF) {

    if (theCube[C1].w == cube[0]){
      if (theCube[C1].o == cube[1]){
        if (theCube[C1].g == cube[2]){
          if (theCube[C2].w == cube[3]){
            if (theCube[C2].b == cube[4]){
              if (theCube[C2].o == cube[5]){
                if (theCube[C3].r == cube[6]){
                  if (theCube[C3].w == cube[7]){
                    if (theCube[C3].g == cube[8]){
                      if (theCube[C4].r == cube[9]){
                        if (theCube[C4].w == cube[10]){
                          if (theCube[C4].b == cube[11]){
                            if (theCube[C5].r == cube[12]){
                              if (theCube[C5].b == cube[13]){
                                if (theCube[C5].y == cube[14]){
                                  if (theCube[C6].b == cube[15]){
                                    if (theCube[C6].o == cube[16]){
                                      if (theCube[C6].y == cube[17]){
                                        if (theCube[C7].r == cube[18]){
                                          if (theCube[C7].y == cube[19]){
                                            if (theCube[C7].g == cube[20]){
                                              if (theCube[C8].o == cube[21]){
                                                if (theCube[C8].y == cube[22]){
                                                  if (theCube[C8].g == cube[23]){
						    if(distToGoal < best)
                                                      best = distToGoal;
                                                  }}}}}}}}}}}}}}}}}}}}}}}}
  }
  return best;
}


void copyCube( struct aCube * newCube, struct aCube * oldCube) {
  newCube[C1].w = oldCube[C1].w;
  newCube[C1].o = oldCube[C1].o;
  newCube[C1].g = oldCube[C1].g;
  newCube[C2].w = oldCube[C2].w;
  newCube[C2].b = oldCube[C2].b;
  newCube[C2].o = oldCube[C2].o;
  newCube[C3].r = oldCube[C3].r;
  newCube[C3].w = oldCube[C3].w;
  newCube[C3].g = oldCube[C3].g;
  newCube[C4].r = oldCube[C4].r;
  newCube[C4].w = oldCube[C4].w;
  newCube[C4].b = oldCube[C4].b;
  newCube[C5].r = oldCube[C5].r;
  newCube[C5].b = oldCube[C5].b;
  newCube[C5].y = oldCube[C5].y;
  newCube[C6].b = oldCube[C6].b;
  newCube[C6].o = oldCube[C6].o;
  newCube[C6].y = oldCube[C6].y;
  newCube[C7].r = oldCube[C7].r;
  newCube[C7].y = oldCube[C7].y;
  newCube[C7].g = oldCube[C7].g;
  newCube[C8].o = oldCube[C8].o;
  newCube[C8].y = oldCube[C8].y;
  newCube[C8].g = oldCube[C8].g;
  newCube[ E9].w = oldCube[ E9].w;
  newCube[ E9].o = oldCube[ E9].o;
  newCube[E10].w = oldCube[E10].w;
  newCube[E10].b = oldCube[E10].b;
  newCube[E11].r = oldCube[E11].r;
  newCube[E11].w = oldCube[E11].w;
  newCube[E12].w = oldCube[E12].w;
  newCube[E12].g = oldCube[E12].g;
  newCube[E13].b = oldCube[E13].b;
  newCube[E13].y = oldCube[E13].y;
  newCube[E14].y = oldCube[E14].y;
  newCube[E14].o = oldCube[E14].o;
  newCube[E15].y = oldCube[E15].y;
  newCube[E15].g = oldCube[E15].g;
  newCube[E16].r = oldCube[E16].r;
  newCube[E16].y = oldCube[E16].y;
  newCube[E17].r = oldCube[E17].r;
  newCube[E17].g = oldCube[E17].g;
  newCube[E18].r = oldCube[E18].r;
  newCube[E18].b = oldCube[E18].b;
  newCube[E19].b = oldCube[E19].b;
  newCube[E19].o = oldCube[E19].o;
  newCube[E20].o = oldCube[E20].o;
  newCube[E20].g = oldCube[E20].g;
}


void resetCube( struct aCube * theCube) {
  struct aCube goalState[20] = {
    { 'Z', 'W', 'Z', 'O', 'Z', 'G'},
    { 'Z', 'W', 'B', 'O', 'Z', 'Z'},
    { 'R', 'W', 'Z', 'Z', 'Z', 'G'},
    { 'R', 'W', 'B', 'Z', 'Z', 'Z'},
    { 'R', 'Z', 'B', 'Z', 'Y', 'Z'},
    { 'Z', 'Z', 'B', 'O', 'Y', 'Z'},
    { 'R', 'Z', 'Z', 'Z', 'Y', 'G'},
    { 'Z', 'Z', 'Z', 'O', 'Y', 'G'},
    { 'Z', 'W', 'Z', 'O', 'Z', 'Z'},
    { 'Z', 'W', 'B', 'Z', 'Z', 'Z'},
    { 'R', 'W', 'Z', 'Z', 'Z', 'Z'},
    { 'Z', 'W', 'Z', 'Z', 'Z', 'G'},
    { 'Z', 'Z', 'B', 'Z', 'Y', 'Z'},
    { 'Z', 'Z', 'Z', 'O', 'Y', 'Z'},
    { 'Z', 'Z', 'Z', 'Z', 'Y', 'G'},
    { 'R', 'Z', 'Z', 'Z', 'Y', 'Z'},
    { 'R', 'Z', 'Z', 'Z', 'Z', 'G'},
    { 'R', 'Z', 'B', 'Z', 'Z', 'Z'},
    { 'Z', 'Z', 'B', 'O', 'Z', 'Z'},
    { 'Z', 'Z', 'Z', 'O', 'Z', 'G'}
  };
  copyCube( theCube, goalState );
}


void generateTables( struct aCube * theCube){
  FILE* cornerFile;
  FILE* edgeFile;
  
  cornerFile = fopen( CORNERFILE, "w"); 
  edgeFile   = fopen( EDGEFILE, "w");                 
  generateCorners( theCube, cornerFile, 0);
  resetCube( theCube );
  generateEdges( theCube, edgeFile, 0); 
  resetCube( theCube );
}


void generateCorners( struct aCube * oldCube, FILE* cornerFile, short currentDepth) {
  struct aCube newCube[20] = {};
  copyCube( newCube, oldCube);

  if(currentDepth==0)
    saveCornerCubie( newCube, cornerFile, 0);

  if( MAXDEPTH+1 == currentDepth ) return;
  else{
    char face[6] = { 'r', 'w', 'b', 'o', 'y', 'g' };
    int i, j;
    struct aCube tempCube[20];
    for( i=0; i<=5; i++) {
      for( j=1; j<=3; j+=TURNTYPE) {
	copyCube( tempCube, newCube ); //psuedo-reset for cube
        rotate( tempCube, face[i], j);
        saveCornerCubie( tempCube, cornerFile, currentDepth+1);
        generateCorners( tempCube, cornerFile, currentDepth+1);
      }
    }
  }
}


void generateEdges( struct aCube * oldCube, FILE* edgeFile, short currentDepth) {
  struct aCube newCube[20] = {};
  copyCube( newCube, oldCube);

  if( currentDepth== 0)
    saveEdgeCubie( newCube, edgeFile, 0);

  if( MAXDEPTH+1 == currentDepth ) return;
  else{
    char face[6] = { 'r', 'w', 'b', 'o', 'y', 'g' };
    int i, j;
    struct aCube tempCube[20];
    for( i=0; i<=5; i++) {
      for( j=1; j<=3; j+=TURNTYPE) {
        copyCube( tempCube, newCube );
        rotate( tempCube, face[i], j);
        saveEdgeCubie( tempCube, edgeFile, currentDepth+1);
        generateEdges( tempCube, edgeFile, currentDepth+1);
      }
    }
  }  
}


void scramble( struct aCube * cube, short n ) {
  srand( (unsigned int)time( NULL ) );
  if (n > MAXDEPTH) printf("Warning: scrambling outside the generated problem space.\n");
  short iter = 0;
  for(iter = 0; iter<n; iter++) {
    short randFace = rand() % 6;
    short randTurn = rand() % (3-TURNTYPE) + 1;
    char face[6] = { 'r', 'w', 'b', 'o', 'y', 'g' };
    rotate(cube, face[randFace], randTurn);
    printf("\nface:%2c rotate:%2d", face[randFace], randTurn ); 
  }
  printf("\n");
}


void allRotations() {
  int i,j;
  char face[6] = { 'r', 'w', 'b', 'o', 'y', 'g' };
  struct aCube localCube[20] = {
    { 'Z', 'W', 'Z', 'O', 'Z', 'G'},
    { 'Z', 'W', 'B', 'O', 'Z', 'Z'},
    { 'R', 'W', 'Z', 'Z', 'Z', 'G'},
    { 'R', 'W', 'B', 'Z', 'Z', 'Z'},
    { 'R', 'Z', 'B', 'Z', 'Y', 'Z'},
    { 'Z', 'Z', 'B', 'O', 'Y', 'Z'},
    { 'R', 'Z', 'Z', 'Z', 'Y', 'G'},
    { 'Z', 'Z', 'Z', 'O', 'Y', 'G'},
    { 'Z', 'W', 'Z', 'O', 'Z', 'Z'},
    { 'Z', 'W', 'B', 'Z', 'Z', 'Z'},
    { 'R', 'W', 'Z', 'Z', 'Z', 'Z'},
    { 'Z', 'W', 'Z', 'Z', 'Z', 'G'},
    { 'Z', 'Z', 'B', 'Z', 'Y', 'Z'},
    { 'Z', 'Z', 'Z', 'O', 'Y', 'Z'},
    { 'Z', 'Z', 'Z', 'Z', 'Y', 'G'},
    { 'R', 'Z', 'Z', 'Z', 'Y', 'Z'},
    { 'R', 'Z', 'Z', 'Z', 'Z', 'G'},
    { 'R', 'Z', 'B', 'Z', 'Z', 'Z'},
    { 'Z', 'Z', 'B', 'O', 'Z', 'Z'},
    { 'Z', 'Z', 'Z', 'O', 'Z', 'G'}
          };
  for( i=0; i<=5; i++) {
      for( j=1; j<=3; j++) {
	  resetCube( localCube);
          printf("\nface:%2c rotate:%2d", face[i], j);
          rotate( localCube, face[i], j);
          printCrossCube( localCube);
      }
  }
}


/*******************************************************************/
/***************** Print / Save State Funcitions *******************/
/*******************************************************************/

void saveCornerCubie( struct aCube * cube, FILE* filename, short distToGoal) {
  fprintf( filename, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%d\n", cube[C1].w, cube[C1].o, cube[C1].g, cube[C2].w, cube[C2].b, cube[C2].o, cube[C3].r, cube[C3].w, cube[C3].g, cube[C4].r, cube[C4].w, cube[C4].b, cube[C5].r, cube[C5].b, cube[C5].y, cube[C6].b, cube[C6].o, cube[C6].y, cube[C7].r, cube[C7].y, cube[C7].g, cube[C8].o, cube[C8].y, cube[C8].g, distToGoal);
}

void saveEdgeCubie( struct aCube * cube, FILE* filename, short distToGoal) {
  fprintf( filename, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%d\n", cube[E9].w, cube[E9].o, cube[E10].w, cube[E10].b, cube[E11].r, cube[E11].w, cube[E12].w, cube[E12].g, cube[E13].b, cube[E13].y, cube[E14].y, cube[E14].o, cube[E15].y, cube[E15].g, cube[E16].r, cube[E16].y, cube[E17].r, cube[E17].g, cube[E18].r, cube[E18].b, cube[E19].b, cube[E19].o, cube[E20].o, cube[E20].g, distToGoal);
}

void printStepCube(struct aCube * cube) {
  printf("\n");
  printf("%c  %c  %c\n", cube[C1].w, cube[E9].w, cube[C2].w );
  printf("%c  W  %c \n", cube[E12].w, cube[E10].w );
  printf("%c  %c  %c \n\n", 
	  cube[C3].w, cube[E11].w, cube[C4].w );

  printf("%c  %c  %c    %c  %c  %c\n", 
	  cube[C3].r, cube[E11].r, cube[C4].r,  
	  cube[C4].b, cube[E10].b, cube[C2].b );
  printf("%c  R  %c    %c  B  %c\n", 
	  cube[E17].r, cube[E18].r, 
	  cube[E18].b, cube[E19].b );
  printf("%c  %c  %c    %c  %c  %c\n\n", 
	  cube[C7].r, cube[E16].r, cube[C5].r, 
	  cube[C5].b, cube[E13].b, cube[C6].b );

  printf("\t   %c  %c  %c    %c  %c  %c\n",  
	  cube[C5].y,  cube[E13].y, cube[C6].y,  
	  cube[C6].o,  cube[E19].o, cube[C2].o );
  printf("\t   %c  Y  %c    %c  O  %c\n", 
	  cube[E16].y, cube[E14].y, 
	  cube[E14].o,  cube[E9].o );
  printf("\t   %c  %c  %c    %c  %c  %c\n\n", 
	  cube[C7].y, cube[E15].y, cube[C8].y, 
	  cube[C8].o, cube[E20].o, cube[C1].o );

  printf("\t\t      %c  %c  %c\n",  
	  cube[C8].g, cube[E20].g, cube[C1].g );
  printf("\t\t      %c  G  %c\n", 
	  cube[E15].g, cube[E12].g );
  printf("\t\t      %c  %c  %c\n",  
	  cube[C7].g, cube[E17].g, cube[C3].g );
  printf("\n");
}


void printCrossCube(struct aCube * cube) {
  printf("\n");
  printf("\t   %c  %c  %c\t       %c  %c  %c\n",
          cube[C8].o,  cube[E14].o, cube[C6].o,
          cube[C2].o,  cube[E9].o, cube[C1].o );
  printf("\t   %c  O  %c\t       %c  O  %c\n",
          cube[E20].o, cube[E19].o,
          cube[E19].o, cube[E20].o  );
  printf("\t   %c  %c  %c\t       %c  %c  %c\n",
          cube[C1].o,  cube[E9].o, cube[C2].o,
          cube[C6].o, cube[E14].o, cube[C8].o );
  printf("\n");
  printf(" %c  %c  %c   %c  %c  %c   %c  %c  %c   %c  %c  %c   %c  %c  %c\n",
          cube[C8].g,  cube[E20].g,  cube[C1].g,
          cube[C1].w,   cube[E9].w,  cube[C2].w,
          cube[C2].b,  cube[E19].b,  cube[C6].b,
          cube[C6].y,  cube[E14].y,  cube[C8].y,
          cube[C8].g,  cube[E20].g,  cube[C1].g );
  printf(" %c  G  %c   %c  W  %c   %c  B  %c   %c  Y  %c   %c  G  %c\n",
          cube[E15].g,  cube[E12].g,
          cube[E12].w,  cube[E10].w,
          cube[E10].b,  cube[E13].b,
          cube[E13].y,  cube[E15].y,
          cube[E15].g,  cube[E12].g );
  printf(" %c  %c  %c   %c  %c  %c   %c  %c  %c   %c  %c  %c   %c  %c  %c\n",
          cube[C7].g,  cube[E17].g,  cube[C3].g,
          cube[C3].w,  cube[E11].w,  cube[C4].w,
          cube[C4].b,  cube[E18].b,  cube[C5].b,
          cube[C5].y,  cube[E16].y,  cube[C7].y,
          cube[C7].g,  cube[E17].g,  cube[C3].g );
  printf("\n");
  printf("\t   %c  %c  %c\t       %c  %c  %c\n",
          cube[C3].r, cube[E11].r, cube[C4].r,
          cube[C5].r, cube[E16].r, cube[C7].r );
  printf("\t   %c  R  %c\t       %c  R  %c\n",
          cube[E17].r, cube[E18].r,
          cube[E18].r, cube[E17].r );
  printf("\t   %c  %c  %c\t       %c  %c  %c\n",
          cube[C7].r, cube[E16].r, cube[C5].r,
          cube[C4].r, cube[E11].r, cube[C3].r  );
  printf("\n");
}


void printEdges(struct aCube * theCube){


  printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", 
	 theCube[E9].w, theCube[E9].o, theCube[E10].w,
	 theCube[E10].b,
	 theCube[E11].r,
	 theCube[E11].w,
	 theCube[E12].w,
	 theCube[E12].g,
	 theCube[E13].b,
	 theCube[E13].y,
	 theCube[E14].y,
	 theCube[E14].o,
	 theCube[E15].y,
	 theCube[E15].g,
	 theCube[E16].r,
	 theCube[E16].y,
	 theCube[E17].r,
	 theCube[E17].g,
	 theCube[E18].r,
	 theCube[E18].b,
	 theCube[E19].b,
	 theCube[E19].o,
	 theCube[E20].o,
	 theCube[E20].g);

}

/*******************************************************************/
/********************* Face Rotation Funcitions ********************/
/*******************************************************************/

void r1c(struct aCube * cube) {
  char temp = cube[C3].r;
  cube[C3].r = cube[C7].r;
  cube[C7].r = cube[C5].r;
  cube[C5].r = cube[C4].r;
  cube[C4].r = temp;

  temp = cube[C3].w;
  cube[C3].w = cube[C7].g;
  cube[C7].g = cube[C5].y;
  cube[C5].y = cube[C4].b;
  cube[C4].b = temp;

  temp = cube[C3].g;
  cube[C3].g = cube[C7].y;
  cube[C7].y = cube[C5].b;
  cube[C5].b = cube[C4].w;
  cube[C4].w = temp;
  
}

void r1e(struct aCube * cube) {
  char temp = cube[E11].r;
  cube[E11].r = cube[E17].r;
  cube[E17].r = cube[E16].r;
  cube[E16].r = cube[E18].r;
  cube[E18].r = temp;

  temp = cube[E11].w;
  cube[E11].w = cube[E17].g;
  cube[E17].g = cube[E16].y;
  cube[E16].y = cube[E18].b;
  cube[E18].b = temp;
}

void r2c(struct aCube * cube) {
  char temp = cube[C3].r;
  cube[C3].r = cube[C5].r;
  cube[C5].r = temp;

  temp = cube[C4].r;
  cube[C4].r = cube[C7].r;
  cube[C7].r = temp;

  temp = cube[C3].w;
  cube[C3].w = cube[C5].y;
  cube[C5].y = temp;

  temp = cube[C3].g;
  cube[C3].g = cube[C5].b; 
  cube[C5].b = temp;

  temp = cube[C4].w;
  cube[C4].w = cube[C7].y;
  cube[C7].y = temp;

  temp = cube[C4].b;
  cube[C4].b = cube[C7].g;
  cube[C7].g = temp;
}

void r2e(struct aCube * cube) {
  char temp = cube[E11].r;
  cube[E11].r = cube[E16].r;
  cube[E16].r = temp;

  temp = cube[E17].r;
  cube[E17].r = cube[E18].r;
  cube[E18].r = temp;

  temp = cube[E11].w;
  cube[E11].w = cube[E16].y;
  cube[E16].y = temp;

  temp = cube[E17].g;
  cube[E17].g = cube[E18].b;
  cube[E18].b = temp;
}

void r3c(struct aCube * cube) {
  char temp = cube[C3].r;
  cube[C3].r = cube[C4].r;
  cube[C4].r = cube[C5].r;
  cube[C5].r = cube[C7].r;
  cube[C7].r = temp;

  temp = cube[C3].w;
  cube[C3].w = cube[C4].b;
  cube[C4].b = cube[C5].y;
  cube[C5].y = cube[C7].g;
  cube[C7].g = temp;

  temp = cube[C3].g;
  cube[C3].g = cube[C4].w;
  cube[C4].w = cube[C5].b;
  cube[C5].b = cube[C7].y;
  cube[C7].y = temp;
}

void r3e(struct aCube * cube) {
  char temp = cube[E11].r;
  cube[E11].r = cube[E18].r;
  cube[E18].r = cube[E16].r;
  cube[E16].r = cube[E17].r;
  cube[E17].r = temp;

  temp = cube[E11].w;
  cube[E11].w = cube[E18].b;
  cube[E18].b = cube[E16].y;
  cube[E16].y = cube[E17].g;
  cube[E17].g = temp;
}



void w1c(struct aCube * cube) {
  char temp = cube[C1].o;
  cube[C1].o = cube[C3].g;
  cube[C3].g = cube[C4].r;
  cube[C4].r = cube[C2].b;
  cube[C2].b = temp;

  temp = cube[C1].g;
  cube[C1].g = cube[C3].r;
  cube[C3].r = cube[C4].b;
  cube[C4].b = cube[C2].o;
  cube[C2].o = temp;

  temp = cube[C1].w;
  cube[C1].w = cube[C3].w;
  cube[C3].w = cube[C4].w;
  cube[C4].w = cube[C2].w;
  cube[C2].w = temp;
}

void w1e(struct aCube * cube) {
  char temp = cube[E9].o;
  cube[E9].o = cube[E12].g;
  cube[E12].g = cube[E11].r;
  cube[E11].r = cube[E10].b;
  cube[E10].b = temp;

  temp = cube[E9].w;
  cube[E9].w = cube[E12].w;
  cube[E12].w = cube[E11].w;
  cube[E11].w = cube[E10].w;
  cube[E10].w = temp;
}

void w2c(struct aCube * cube) {
  char temp = cube[C1].o;
  cube[C1].o = cube[C4].r;
  cube[C4].r = temp;
  temp = cube[C1].g;
  cube[C1].g = cube[C4].b;
  cube[C4].b = temp;

  temp = cube[C2].b;
  cube[C2].b = cube[C3].g;
  cube[C3].g = temp;
  temp = cube[C2].o;
  cube[C2].o = cube[C3].r;
  cube[C3].r = temp;

  temp = cube[C1].w;
  cube[C1].w = cube[C4].w;
  cube[C4].w = temp;
  temp = cube[C2].w;
  cube[C2].w = cube[C3].w;
  cube[C3].w = temp;
}

void w2e(struct aCube * cube) {
  char temp = cube[E9].o;
  cube[E9].o = cube[E11].r;
  cube[E11].r = temp;
  temp = cube[E12].g; 
  cube[E12].g = cube[E10].b;
  cube[E10].b = temp;
  
  temp = cube[E9].w;
  cube[E9].w = cube[E11].w;
  cube[E11].w = temp;
  temp = cube[E12].w;
  cube[E12].w = cube[E10].w;
  cube[E10].w = temp;
}

void w3c(struct aCube * cube) {
  char temp = cube[C1].o;
  cube[C1].o = cube[C2].b;
  cube[C2].b = cube[C4].r;
  cube[C4].r = cube[C3].g;
  cube[C3].g = temp; 

  temp = cube[C1].g;
  cube[C1].g = cube[C2].o;
  cube[C2].o = cube[C4].b;
  cube[C4].b = cube[C3].r;
  cube[C3].r = temp;

  temp = cube[C1].w;
  cube[C1].w = cube[C2].w;
  cube[C2].w = cube[C4].w;
  cube[C4].w = cube[C3].w;
  cube[C3].w = temp;
}

void w3e(struct aCube * cube) {
  char temp  = cube[E9].o;
  cube[E9].o  = cube[E10].b;
  cube[E10].b = cube[E11].r;
  cube[E11].r = cube[E12].g;
  cube[E12].g = temp;

  temp = cube[E9].w;
  cube[E9].w  = cube[E10].w;
  cube[E10].w = cube[E11].w;
  cube[E11].w = cube[E12].w;
  cube[E12].w = temp;
}


void b1c(struct aCube * cube) {
  char temp = cube[C4].b;
  cube[C4].b = cube[C5].b;
  cube[C5].b = cube[C6].b;
  cube[C6].b = cube[C2].b;
  cube[C2].b = temp;

  temp = cube[C4].w;
  cube[C4].w = cube[C5].r;
  cube[C5].r = cube[C6].y;
  cube[C6].y = cube[C2].o;
  cube[C2].o = temp;

  temp = cube[C4].r;
  cube[C4].r = cube[C5].y;
  cube[C5].y = cube[C6].o;
  cube[C6].o = cube[C2].w;
  cube[C2].w = temp;
}

void b1e(struct aCube * cube) {
  char temp = cube[E10].b;
  cube[E10].b = cube[E18].b;
  cube[E18].b = cube[E13].b;
  cube[E13].b = cube[E19].b;
  cube[E19].b = temp;
  
  temp = cube[E10].w;
  cube[E10].w = cube[E18].r;
  cube[E18].r = cube[E13].y;
  cube[E13].y = cube[E19].o;
  cube[E19].o = temp;
}

void b2c(struct aCube * cube) {
  char temp = cube[C4].b;
  cube[C4].b = cube[C6].b;
  cube[C6].b = temp;
  temp = cube[C2].b;
  cube[C2].b = cube[C5].b;
  cube[C5].b = temp;

  temp = cube[C4].w;
  cube[C4].w = cube[C6].y;
  cube[C6].y = temp;
  temp = cube[C4].r;
  cube[C4].r = cube[C6].o;
  cube[C6].o = temp;

  temp = cube[C2].w;
  cube[C2].w = cube[C5].y;
  cube[C5].y = temp;
  temp = cube[C2].o;
  cube[C2].o = cube[C5].r;
  cube[C5].r = temp;
}

void b2e(struct aCube * cube) {
  char temp = cube[E10].b;
  cube[E10].b = cube[E13].b;
  cube[E13].b = temp;
  temp = cube[E18].b;
  cube[E18].b = cube[E19].b;
  cube[E19].b = temp;

  temp = cube[E10].w;
  cube[E10].w = cube[E13].y;
  cube[E13].y = temp;
  temp = cube[E18].r;
  cube[E18].r = cube[E19].o;
  cube[E19].o = temp;
}

void b3c(struct aCube * cube) {
  char temp = cube[C4].b;
  cube[C4].b = cube[C2].b;
  cube[C2].b = cube[C6].b;
  cube[C6].b = cube[C5].b;
  cube[C5].b = temp;

  temp = cube[C4].w;
  cube[C4].w = cube[C2].o;
  cube[C2].o = cube[C6].y;
  cube[C6].y = cube[C5].r;
  cube[C5].r = temp;

  temp = cube[C4].r;
  cube[C4].r = cube[C2].w;
  cube[C2].w = cube[C6].o;
  cube[C6].o = cube[C5].y;
  cube[C5].y = temp;
}

void b3e(struct aCube * cube) {
  char temp = cube[E10].b;
  cube[E10].b = cube[E19].b;
  cube[E19].b = cube[E13].b;
  cube[E13].b = cube[E18].b;
  cube[E18].b = temp;

  temp = cube[E10].w;
  cube[E10].w = cube[E19].o;
  cube[E19].o = cube[E13].y;
  cube[E13].y = cube[E18].r;
  cube[E18].r = temp;
}



void o1c(struct aCube * cube) {
  char temp = cube[C6].o;
  cube[C6].o = cube[C8].o;
  cube[C8].o = cube[C1].o;
  cube[C1].o = cube[C2].o;
  cube[C2].o = temp;

  temp = cube[C6].b;
  cube[C6].b = cube[C8].y;
  cube[C8].y = cube[C1].g;
  cube[C1].g = cube[C2].w;
  cube[C2].w = temp;

  temp = cube[C6].y; 
  cube[C6].y = cube[C8].g;
  cube[C8].g = cube[C1].w;
  cube[C1].w = cube[C2].b;
  cube[C2].b = temp;
}

void o1e(struct aCube * cube) {
  char temp = cube[E19].o;
  cube[E19].o = cube[E14].o;
  cube[E14].o = cube[E20].o;
  cube[E20].o = cube[E9].o;
  cube[E9].o = temp;
  
  temp = cube[E19].b;
  cube[E19].b = cube[E14].y;
  cube[E14].y = cube[E20].g;
  cube[E20].g = cube[E9].w;
  cube[E9].w = temp;
}

void o2c(struct aCube * cube) {
  char temp = cube[C6].o;
  cube[C6].o = cube[C1].o;
  cube[C1].o = temp;

  temp = cube[C2].o;
  cube[C2].o = cube[C8].o;
  cube[C8].o = temp;

  temp = cube[C6].b;
  cube[C6].b = cube[C1].g;
  cube[C1].g = temp;

  temp = cube[C6].y;
  cube[C6].y = cube[C1].w;
  cube[C1].w = temp;

  temp = cube[C2].b;
  cube[C2].b = cube[C8].g;
  cube[C8].g = temp;

  temp = cube[C2].w;
  cube[C2].w = cube[C8].y;
  cube[C8].y = temp;
}

void o2e(struct aCube * cube) {
  char temp = cube[E19].o;
  cube[E19].o = cube[E20].o;
  cube[E20].o = temp;

  temp = cube[E14].o;
  cube[E14].o = cube[E9].o;
  cube[E9].o = temp;

  temp = cube[E19].b;
  cube[E19].b = cube[E20].g;
  cube[E20].g = temp;

  temp = cube[E14].y;
  cube[E14].y = cube[E9].w;
  cube[E9].w = temp;
}

void o3c(struct aCube * cube) {
  char temp = cube[C6].o;
  cube[C6].o = cube[C2].o;
  cube[C2].o = cube[C1].o;
  cube[C1].o = cube[C8].o;
  cube[C8].o = temp;

  temp = cube[C6].b;
  cube[C6].b = cube[C2].w;
  cube[C2].w = cube[C1].g; 
  cube[C1].g = cube[C8].y;
  cube[C8].y = temp;

  temp = cube[C6].y;
  cube[C6].y = cube[C2].b; 
  cube[C2].b = cube[C1].w;
  cube[C1].w = cube[C8].g;
  cube[C8].g = temp;
}

void o3e(struct aCube * cube) {
  char temp = cube[E19].o;
  cube[E19].o = cube[E9].o;
  cube[E9].o  = cube[E20].o;
  cube[E20].o = cube[E14].o;
  cube[E14].o = temp;

  temp = cube[E19].b;
  cube[E19].b = cube[E9].w;
  cube[E9].w  = cube[E20].g;
  cube[E20].g  = cube[E14].y;
  cube[E14].y =temp;
}


void y1c(struct aCube * cube) {
  char temp = cube[C8].y;
  cube[C8].y = cube[C6].y;
  cube[C6].y = cube[C5].y;
  cube[C5].y = cube[C7].y;
  cube[C7].y = temp;

  temp = cube[C8].o;
  cube[C8].o = cube[C6].b;
  cube[C6].b = cube[C5].r;
  cube[C5].r = cube[C7].g;
  cube[C7].g = temp;

  temp = cube[C8].g;
  cube[C8].g = cube[C6].o;
  cube[C6].o = cube[C5].b;
  cube[C5].b = cube[C7].r;
  cube[C7].r = temp;
}

void y1e(struct aCube * cube) {
  char temp = cube[E14].y;
  cube[E14].y = cube[E13].y;
  cube[E13].y = cube[E16].y;
  cube[E16].y = cube[E15].y;
  cube[E15].y = temp;

  temp = cube[E14].o;
  cube[E14].o = cube[E13].b;
  cube[E13].b = cube[E16].r;
  cube[E16].r = cube[E15].g;
  cube[E15].g = temp;
}

void y2c(struct aCube * cube) {
  char temp = cube[C6].y;
  cube[C6].y = cube[C7].y;
  cube[C7].y = temp;
  temp = cube[C5].y;
  cube[C5].y = cube[C8].y;
  cube[C8].y = temp;

  temp = cube[C6].o;
  cube[C6].o = cube[C7].r;
  cube[C7].r = temp;
  temp = cube[C5].b;
  cube[C5].b = cube[C8].g;
  cube[C8].g = temp;

  temp = cube[C6].b;
  cube[C6].b = cube[C7].g;
  cube[C7].g = temp;
  temp = cube[C5].r;
  cube[C5].r = cube[C8].o;
  cube[C8].o = temp;
}

void y2e(struct aCube * cube) {
  char temp = cube[E14].y;
  cube[E14].y = cube[E16].y;
  cube[E16].y = temp;
  temp = cube[E13].y;
  cube[E13].y = cube[E15].y;
  cube[E15].y = temp;

  temp = cube[E14].o;
  cube[E14].o = cube[E16].r;
  cube[E16].r = temp;
  temp = cube[E13].b;
  cube[E13].b = cube[E15].g;
  cube[E15].g = temp;
}

void y3c(struct aCube * cube) {
  char temp = cube[C8].y;
  cube[C8].y = cube[C7].y;
  cube[C7].y = cube[C5].y;
  cube[C5].y = cube[C6].y;
  cube[C6].y = temp;

  temp = cube[C8].o;
  cube[C8].o = cube[C7].g;
  cube[C7].g = cube[C5].r;
  cube[C5].r = cube[C6].b;
  cube[C6].b = temp;

  temp = cube[C8].g;
  cube[C8].g = cube[C7].r;
  cube[C7].r = cube[C5].b;
  cube[C5].b = cube[C6].o;
  cube[C6].o = temp;
}

void y3e(struct aCube * cube) {
  char temp = cube[E14].y;
  cube[E14].y = cube[E15].y;
  cube[E15].y = cube[E16].y;
  cube[E16].y = cube[E13].y;
  cube[E13].y = temp;

  temp = cube[E14].o;
  cube[E14].o = cube[E15].g;
  cube[E15].g = cube[E16].r;
  cube[E16].r = cube[E13].b;
  cube[E13].b = temp;
}



void g1c(struct aCube * cube) {
  char temp = cube[C8].g;
  cube[C8].g = cube[C7].g;
  cube[C7].g = cube[C3].g;
  cube[C3].g = cube[C1].g;
  cube[C1].g = temp;

  temp = cube[C8].y;
  cube[C8].y = cube[C7].r;
  cube[C7].r = cube[C3].w;
  cube[C3].w = cube[C1].o;
  cube[C1].o = temp;

  temp = cube[C8].o;
  cube[C8].o = cube[C7].y;
  cube[C7].y = cube[C3].r;
  cube[C3].r = cube[C1].w;
  cube[C1].w = temp;
}

void g1e(struct aCube * cube) {
  char temp = cube[E20].g;
  cube[E20].g = cube[E15].g;
  cube[E15].g = cube[E17].g;
  cube[E17].g = cube[E12].g;
  cube[E12].g = temp;

  temp = cube[E20].o;
  cube[E20].o = cube[E15].y;
  cube[E15].y = cube[E17].r;
  cube[E17].r = cube[E12].w;
  cube[E12].w = temp;
}

void g2c(struct aCube * cube) {
  char temp = cube[C1].g;
  cube[C1].g = cube[C7].g;
  cube[C7].g = temp;
  temp = cube[C3].g;
  cube[C3].g = cube[C8].g;
  cube[C8].g = temp;

  temp = cube[C1].o;
  cube[C1].o = cube[C7].r;
  cube[C7].r = temp;
  temp = cube[C3].w;
  cube[C3].w = cube[C8].y;
  cube[C8].y = temp;

  temp = cube[C1].w;
  cube[C1].w = cube[C7].y;
  cube[C7].y = temp;
  temp = cube[C3].r;
  cube[C3].r = cube[C8].o;
  cube[C8].o = temp;
}

void g2e(struct aCube * cube) {
  char temp = cube[E20].g;
  cube[E20].g = cube[E17].g;
  cube[E17].g = temp;
  temp = cube[E12].g;
  cube[E12].g = cube[E15].g;
  cube[E15].g = temp;

  temp = cube[E20].o;
  cube[E20].o = cube[E17].r;
  cube[E17].r = temp;
  temp = cube[E12].w;
  cube[E12].w = cube[E15].y;
  cube[E15].y = temp;
}

void g3c(struct aCube * cube) {
  char temp = cube[C1].g;
  cube[C1].g = cube[C3].g;
  cube[C3].g = cube[C7].g;
  cube[C7].g = cube[C8].g;
  cube[C8].g = temp;

  temp = cube[C1].o;
  cube[C1].o = cube[C3].w;
  cube[C3].w = cube[C7].r;
  cube[C7].r = cube[C8].y;
  cube[C8].y = temp;

  temp = cube[C1].w;
  cube[C1].w = cube[C3].r;
  cube[C3].r = cube[C7].y;
  cube[C7].y = cube[C8].o;
  cube[C8].o = temp;
}

void g3e(struct aCube * cube) {
  char temp = cube[E12].g;
  cube[E12].g = cube[E17].g;
  cube[E17].g = cube[E15].g;
  cube[E15].g = cube[E20].g;
  cube[E20].g = temp;

  temp = cube[E12].w;
  cube[E12].w = cube[E17].r;
  cube[E17].r = cube[E15].y;
  cube[E15].y = cube[E20].o;
  cube[E20].o = temp;
}


void rotate( struct aCube * cube, char f, int t) {
  switch (f){

  case 'r':
    switch (t){
    case 1:
      r1c( cube );
      r1e( cube );
      break;
    case 2: 
      r2c( cube );
      r2e( cube );
      break;
    case 3:
      r3c( cube );
      r3e( cube );
      break;
    }
  break;

  case 'w':
    switch (t){
    case 1: 
      w1c( cube );
      w1e( cube );
      break;
    case 2:
      w2c( cube );
      w2e( cube );
      break;
    case 3:
      w3c( cube );
      w3e( cube );
      break;
    }
    break;

  case 'b':
    switch (t){
    case 1: 
      b1c( cube );
      b1e( cube );
      break;
    case 2:
      b2c( cube );
      b2e( cube );
      break;
    case 3:
      b3c( cube );
      b3e( cube );
      break;
    }
    break;

  case 'o':
    switch (t){
    case 1: 
      o1c( cube );
      o1e( cube );
      break;
    case 2:
      o2c( cube );
      o2e( cube );
      break;
    case 3:
      o3c( cube );
      o3e( cube );
      break;
    }
    break;

  case 'y':
    switch (t){
    case 1:
      y1c( cube );
      y1e( cube );
      break;
    case 2:
      y2c( cube );
      y2e( cube );
      break;
    case 3:
      y3c( cube );
      y3e( cube );
      break;
    }
    break;

  case 'g':
    switch (t){
    case 1:
      g1c( cube );
      g1e( cube );
      break;
    case 2:
      g2c( cube );
      g2e( cube );
      break;
    case 3:
      g3c( cube );
      g3e( cube );
      break;
    }
    break;
  }
}

//Bottom of page.
